home *** CD-ROM | disk | FTP | other *** search
/ Linux Cubed Series 7: Sunsite / Linux Cubed Series 7 - Sunsite Vol 1.iso / system / shells / rc-1.000 / rc-1 / rc-1.5-linux / builtins.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-07-28  |  11.6 KB  |  561 lines

  1. /* builtins.c: the collection of rc's builtin commands */
  2.  
  3. /*
  4.     NOTE: rc's builtins do not call "rc_error" because they are
  5.     commands, and rc errors usually arise from syntax errors. e.g.,
  6.     you probably don't want interpretation of a shell script to stop
  7.     because of a bad umask.
  8. */
  9.  
  10. #include <sys/ioctl.h>
  11. #include <setjmp.h>
  12. #include <errno.h>
  13. #include "rc.h"
  14. #include "jbwrap.h"
  15. #include "sigmsgs.h"
  16. #ifndef NOLIMITS
  17. #include <sys/time.h>
  18. #include <sys/resource.h>
  19. #endif
  20. #include "addon.h"
  21.  
  22. extern int umask(int);
  23.  
  24. static void b_break(char **), b_cd(char **), b_eval(char **), b_exit(char **),
  25.     b_newpgrp(char **), b_return(char **), b_shift(char **), b_umask(char **),
  26.     b_wait(char **), b_whatis(char **);
  27.  
  28. #ifndef NOLIMITS
  29. static void b_limit(char **);
  30. #endif
  31. #ifndef NOECHO
  32. static void b_echo(char **);
  33. #endif
  34.  
  35. static struct {
  36.     builtin_t *p;
  37.     char *name;
  38. } builtins[] = {
  39.     { b_break,    "break" },
  40.     { b_builtin,    "builtin" },
  41.     { b_cd,        "cd" },
  42. #ifndef NOECHO
  43.     { b_echo,    "echo" },
  44. #endif
  45.     { b_eval,    "eval" },
  46.     { b_exec,    "exec" },
  47.     { b_exit,    "exit" },
  48. #ifndef NOLIMITS
  49.     { b_limit,    "limit" },
  50. #endif
  51.     { b_newpgrp,    "newpgrp" },
  52.     { b_return,    "return" },
  53.     { b_shift,    "shift" },
  54.     { b_umask,    "umask" },
  55.     { b_wait,    "wait" },
  56.     { b_whatis,    "whatis" },
  57.     { b_dot,    "." }
  58. #ifdef ADDONS
  59.         , ADDONS
  60. #endif
  61. };
  62.  
  63. extern builtin_t *isbuiltin(char *s) {
  64.     int i;
  65.     for (i = 0; i < arraysize(builtins); i++)
  66.         if (streq(builtins[i].name, s))
  67.             return builtins[i].p;
  68.     return NULL;
  69. }
  70.  
  71. /* funcall() is the wrapper used to invoke shell functions. pushes $*, and "return" returns here. */
  72.  
  73. extern void funcall(char **av) {
  74.     Jbwrap j;
  75.     Estack e1, e2;
  76.     Edata jreturn, star;
  77.     if (setjmp(j.j))
  78.         return;
  79.     starassign(*av, av+1, TRUE);
  80.     jreturn.jb = &j;
  81.     star.name = "*";
  82.     except(eReturn, jreturn, &e1);
  83.     except(eVarstack, star, &e2);
  84.     walk(treecpy(fnlookup(*av), nalloc), TRUE);
  85.     varrm("*", TRUE);
  86.     unexcept(); /* eVarstack */
  87.     unexcept(); /* eReturn */
  88. }
  89.  
  90. static void arg_count(char *name) {
  91.     fprint(2, "too many arguments to %s\n", name);
  92.     set(FALSE);
  93. }
  94.  
  95. static void badnum(char *num) {
  96.     fprint(2, "%s is a bad number\n", num);
  97.     set(FALSE);
  98. }
  99.  
  100. /* a dummy command. (exec() performs "exec" simply by not forking) */
  101.  
  102. extern void b_exec(char **av) {
  103. }
  104.  
  105. #ifndef NOECHO
  106. /* echo -n omits a newline. echo -- -n echos '-n' */
  107.  
  108. static void b_echo(char **av) {
  109.     char *format = "%A\n";
  110.     if (*++av != NULL) {
  111.         if (streq(*av, "-n"))
  112.                     format = "%A", av++;
  113.         else if (streq(*av, "--"))
  114.             av++;
  115.     }
  116.     fprint(1, format, av);
  117.     set(TRUE);
  118. }
  119. #endif
  120.  
  121. /* cd. traverse $cdpath if the directory given is not an absolute pathname */
  122.  
  123. static void b_cd(char **av) {
  124.     List *s, nil;
  125.     char *path = NULL;
  126.     SIZE_T t, pathlen = 0;
  127.     if (*++av == NULL) {
  128.         s = varlookup("home");
  129.         *av = (s == NULL) ? "/" : s->w;
  130.     } else if (av[1] != NULL) {
  131.         arg_count("cd");
  132.         return;
  133.     }
  134.     if (isabsolute(*av) || streq(*av, ".") || streq(*av, "..")) { /* absolute pathname? */
  135.         if (chdir(*av) < 0) {
  136.             set(FALSE);
  137.             uerror(*av);
  138.         } else
  139.             set(TRUE);
  140.     } else {
  141.         s = varlookup("cdpath");
  142.         if (s == NULL) {
  143.             s = &nil;
  144.             nil.w = "";
  145.             nil.n = NULL;
  146.         }
  147.         do {
  148.             if (s != &nil && *s->w != '\0') {
  149.                 t = strlen(*av) + strlen(s->w) + 2;
  150.                 if (t > pathlen)
  151.                     path = nalloc(pathlen = t);
  152.                 strcpy(path, s->w);
  153.                 if (!streq(s->w, "/")) /* "//" is special to POSIX */
  154.                     strcat(path, "/");
  155.                 strcat(path, *av);
  156.             } else {
  157.                 pathlen = 0;
  158.                 path = *av;
  159.             }
  160.             if (chdir(path) >= 0) {
  161.                 set(TRUE);
  162. /*                if (interactive && *s->w != '\0' && !streq(s->w, "."))
  163.                     fprint(1, "%s\n", path);
  164. */                return;
  165.             }
  166.             s = s->n;
  167.         } while (s != NULL);
  168.         fprint(2, "couldn't cd to %s\n", *av);
  169.         set(FALSE);
  170.     }
  171. }
  172.  
  173. static void b_umask(char **av) {
  174.     int i;
  175.     if (*++av == NULL) {
  176.         set(TRUE);
  177.         i = umask(0);
  178.         umask(i);
  179.         fprint(1, "0%o\n", i);
  180.     } else if (av[1] == NULL) {
  181.         i = o2u(*av);
  182.         if ((unsigned int) i > 0777) {
  183.             fprint(2, "bad umask\n");
  184.             set(FALSE);
  185.         } else {
  186.             umask(i);
  187.             set(TRUE);
  188.         }
  189.     } else {
  190.         arg_count("umask");
  191.         return;
  192.     }
  193. }
  194.  
  195. static void b_exit(char **av) {
  196.     if (*++av != NULL)
  197.         ssetstatus(av);
  198.     rc_exit(getstatus());
  199. }
  200.  
  201. /* raise a "return" exception, i.e., return from a function. if an integer argument is present, set $status to it */
  202.  
  203. static void b_return(char **av) {
  204.     if (*++av != NULL)
  205.         ssetstatus(av);
  206.     rc_raise(eReturn);
  207. }
  208.  
  209. /* raise a "break" exception for breaking out of for and while loops */
  210.  
  211. static void b_break(char **av) {
  212.     if (av[1] != NULL) {
  213.         arg_count("break");
  214.         return;
  215.     }
  216.     rc_raise(eBreak);
  217. }
  218.  
  219. /* shift $* n places (default 1) */
  220.  
  221. static void b_shift(char **av) {
  222.     int shift = (av[1] == NULL ? 1 : a2u(av[1]));
  223.     List *s, *dollarzero;
  224.     if (av[1] != NULL && av[2] != NULL) {
  225.         arg_count("shift");
  226.         return;
  227.     }
  228.     if (shift < 0) {
  229.         badnum(av[1]);
  230.         return;
  231.     }
  232.     s = varlookup("*")->n;
  233.     dollarzero = varlookup("0");
  234.     while (s != NULL && shift != 0) {
  235.         s = s->n;
  236.         --shift;
  237.     }
  238.     if (s == NULL && shift != 0) {
  239.         fprint(2, "cannot shift\n");
  240.         set(FALSE);
  241.     } else {
  242.         varassign("*", append(dollarzero, s), FALSE);
  243.         set(TRUE);
  244.     }
  245. }
  246.  
  247. /* dud function */
  248.  
  249. extern void b_builtin(char **av) {
  250. }
  251.  
  252. /* wait for a given process, or all outstanding processes */
  253.  
  254. static void b_wait(char **av) {
  255.     int stat, pid;
  256.     if (av[1] == NULL) {
  257.         waitforall();
  258.         return;
  259.     }
  260.     if (av[2] != NULL) {
  261.         arg_count("wait");
  262.         return;
  263.     }
  264.     if ((pid = a2u(av[1])) < 0) {
  265.         badnum(av[1]);
  266.         return;
  267.     }
  268.     if (rc_wait4(pid, &stat, FALSE) > 0)
  269.         setstatus(pid, stat);
  270.     else
  271.         set(FALSE);
  272.     sigchk();
  273. }
  274.  
  275. /*
  276.    whatis without arguments prints all variables and functions. Otherwise, check to see if a name
  277.    is defined as a variable, function or pathname.
  278. */
  279.  
  280. #define not(b)    ((b)^TRUE)
  281. #define show(b)    (not(eff|vee|pee|bee|ess)|(b))
  282.  
  283. static bool issig(char *s) {
  284.     int i;
  285.     for (i = 0; i < NUMOFSIGNALS; i++)
  286.         if (streq(s, signals[i].name))
  287.             return TRUE;
  288.     return FALSE;
  289. }
  290.  
  291. static void b_whatis(char **av) {
  292.     bool ess, eff, vee, pee, bee;
  293.     bool f, found;
  294.     int i, ac, c;
  295.     List *s;
  296.     Node *n;
  297.     char *e;
  298.     for (rc_optind = ac = 0; av[ac] != NULL; ac++)
  299.         ; /* count the arguments for getopt */
  300.     ess = eff = vee = pee = bee = FALSE;
  301.     while ((c = rc_getopt(ac, av, "sfvpb")) != -1)
  302.         switch (c) {
  303.         default: set(FALSE); return;
  304.         case 's': ess = TRUE; break;
  305.         case 'f': eff = TRUE; break;
  306.         case 'v': vee = TRUE; break;
  307.         case 'p': pee = TRUE; break;
  308.         case 'b': bee = TRUE; break;
  309.         }
  310.     av += rc_optind;
  311.     if (*av == NULL) {
  312.         if (vee|eff)
  313.             whatare_all_vars(eff, vee);
  314.         if (ess)
  315.             whatare_all_signals();
  316.         if (bee)
  317.             for (i = 0; i < arraysize(builtins); i++)
  318.                 fprint(1, "builtin %s\n", builtins[i].name);
  319.         if (pee)
  320.             fprint(2, "whatis -p: must specify argument\n");
  321.         if (show(FALSE)) /* no options? */
  322.             whatare_all_vars(TRUE, TRUE);
  323.         set(TRUE);
  324.         return;
  325.     }
  326.     found = TRUE;
  327.     for (i = 0; av[i] != NULL; i++) {
  328.         f = FALSE;
  329.         errno = ENOENT;
  330.         if (show(vee) && (s = varlookup(av[i])) != NULL) {
  331.             f = TRUE;
  332.             prettyprint_var(1, av[i], s);
  333.         }
  334.         if (((show(ess)&&issig(av[i])) || show(eff)) && (n = fnlookup(av[i])) != NULL) {
  335.             f = TRUE;
  336.             prettyprint_fn(1, av[i], n);
  337.         } else if (show(bee) && isbuiltin(av[i]) != NULL) {
  338.             f = TRUE;
  339.             fprint(1, "builtin %s\n", av[i]);
  340.         } else if (show(pee) && (e = which(av[i], FALSE)) != NULL) {
  341.             f = TRUE;
  342.             fprint(1, "%S\n", e);
  343.         }
  344.         if (!f) {
  345.             found = FALSE;
  346.             if (errno != ENOENT)
  347.                 uerror(av[i]);
  348.             else
  349.                 fprint(2, "%s not found\n", av[i]);
  350.         }
  351.     }
  352.     set(found);
  353. }
  354.  
  355. /* push a string to be eval'ed onto the input stack. evaluate it */
  356.  
  357. static void b_eval(char **av) {
  358.     bool i = interactive;
  359.     if (av[1] == NULL)
  360.         return;
  361.     interactive = FALSE;
  362.     pushstring(av + 1, i); /* don't reset line numbers on noninteractive eval */
  363.     doit(TRUE);
  364.     interactive = i;
  365. }
  366.  
  367. /*
  368.    push a file to be interpreted onto the input stack. with "-i" treat this as an interactive
  369.    input source.
  370. */
  371.  
  372. extern void b_dot(char **av) {
  373.     int fd;
  374.     bool old_i = interactive, i = FALSE;
  375.     Estack e;
  376.     Edata star;
  377.     av++;
  378.     if (*av == NULL)
  379.         return;
  380.     if (streq(*av, "-i")) {
  381.         av++;
  382.         i = TRUE;
  383.     }
  384.     if (dasheye) { /* rc -i file has to do the right thing. reset the dasheye state to FALSE, though. */
  385.         dasheye = FALSE;
  386.         i = TRUE;
  387.     }
  388.     if (*av == NULL)
  389.         return;
  390.     fd = rc_open(*av, rFrom);
  391.     if (fd < 0) {
  392.         if (rcrc) /* on rc -l, don't flag nonexistence of .rcrc */
  393.             rcrc = FALSE;
  394.         else {
  395.             uerror(*av);
  396.             set(FALSE);
  397.         }
  398.         return;
  399.     }
  400.     rcrc = FALSE;
  401.     starassign(*av, av+1, TRUE);
  402.     pushfd(fd);
  403.     interactive = i;
  404.     star.name = "*";
  405.     except(eVarstack, star, &e);
  406.     doit(TRUE);
  407.     varrm("*", TRUE);
  408.     unexcept(); /* eVarstack */
  409.     interactive = old_i;
  410. }
  411.  
  412. /* put rc into a new pgrp. Used on the NeXT where the Terminal program is broken (sigh) */
  413.  
  414. static void b_newpgrp(char **av) {
  415.     if (av[1] != NULL) {
  416.         arg_count("newpgrp");
  417.         return;
  418.     }
  419.     setpgrp(rc_pid, rc_pid);
  420. #ifdef TIOCSPGRP
  421.     ioctl(2, TIOCSPGRP, &rc_pid);
  422. #endif
  423. }
  424.  
  425. /* Berkeley limit support was cleaned up by Paul Haahr. */
  426.  
  427. #ifndef NOLIMITS
  428. typedef struct Suffix Suffix;
  429. struct Suffix {
  430.     const Suffix *next;
  431.     long amount;
  432.     char *name;
  433. };
  434.  
  435. static const Suffix
  436.     kbsuf = { NULL, 1024, "k" },
  437.     mbsuf = { &kbsuf, 1024*1024, "m" },
  438.     gbsuf = { &mbsuf, 1024*1024*1024, "g" },
  439.     stsuf = { NULL, 1, "s" },
  440.     mtsuf = { &stsuf, 60, "m" },
  441.     htsuf = { &mtsuf, 60*60, "h" };
  442. #define    SIZESUF &gbsuf
  443. #define    TIMESUF &htsuf
  444. #define    NOSUF ((Suffix *) NULL)  /* for RLIMIT_NOFILE on SunOS 4.1 */
  445.  
  446. typedef struct {
  447.     char *name;
  448.     int flag;
  449.     const Suffix *suffix;
  450. } Limit;
  451. static const Limit limits[] = {
  452.     { "cputime",        RLIMIT_CPU,    TIMESUF },
  453.     { "filesize",        RLIMIT_FSIZE,    SIZESUF },
  454.     { "datasize",        RLIMIT_DATA,    SIZESUF },
  455.     { "stacksize",        RLIMIT_STACK,    SIZESUF },
  456.     { "coredumpsize",    RLIMIT_CORE,    SIZESUF },
  457. #ifdef RLIMIT_RSS /* SysVr4 does not have this */
  458.     { "memoryuse",        RLIMIT_RSS,    SIZESUF },
  459. #endif
  460. #ifdef RLIMIT_VMEM /* instead, they have this! */
  461.     { "vmemory",        RLIMIT_VMEM,    SIZESUF },
  462. #endif
  463. #ifdef RLIMIT_NOFILE  /* SunOS 4.1 adds a limit on file descriptors */
  464.     { "descriptors",    RLIMIT_NOFILE,    NOSUF },
  465. #endif
  466.     { NULL, 0, NULL }
  467. };
  468.  
  469. #ifndef SYSVR4
  470. extern int getrlimit(int, struct rlimit *);
  471. extern int setrlimit(int, struct rlimit *);
  472. #endif
  473.  
  474. static void printlimit(const Limit *limit, bool hard) {
  475.     struct rlimit rlim;
  476.     long lim;
  477.     getrlimit(limit->flag, &rlim);
  478.     if (hard)
  479.         lim = rlim.rlim_max;
  480.     else
  481.         lim = rlim.rlim_cur;
  482.     if (lim == RLIM_INFINITY)
  483.         fprint(1, "%s \tunlimited\n", limit->name);
  484.     else {
  485.         const Suffix *suf;
  486.         for (suf = limit->suffix; suf != NULL; suf = suf->next)
  487.             if (lim % suf->amount == 0 && (lim != 0 || suf->amount > 1)) {
  488.                 lim /= suf->amount;
  489.                 break;
  490.             }
  491.         fprint(1, "%s \t%d%s\n", limit->name, lim, (suf == NULL || lim == 0) ? "" : suf->name);
  492.     }
  493. }
  494.  
  495. static long parselimit(const Limit *limit, char *s) {
  496.     char *t;
  497.     int len = strlen(s);
  498.     long lim = 1;
  499.     const Suffix *suf = limit->suffix;
  500.     if (streq(s, "unlimited"))
  501.         return RLIM_INFINITY;
  502.     if (suf == TIMESUF && (t = strchr(s, ':')) != NULL) {
  503.         *t++ = '\0';
  504.         lim = 60 * a2u(s) + a2u(t);
  505.     } else {
  506.         for (; suf != NULL; suf = suf->next)
  507.             if (streq(suf->name, s + len - strlen(suf->name))) {
  508.                 s[len - strlen(suf->name)] = '\0';
  509.                 lim *= suf->amount;
  510.                 break;
  511.             }
  512.         lim *= a2u(s);
  513.     }
  514.     return lim;
  515. }
  516.  
  517. static void b_limit(char **av) {
  518.     const Limit *lp = limits;
  519.     bool hard = FALSE;
  520.     if (*++av != NULL && streq(*av, "-h")) {
  521.         av++;
  522.         hard = TRUE;
  523.     }
  524.     if (*av == NULL) {
  525.         for (; lp->name != NULL; lp++)
  526.             printlimit(lp, hard);
  527.         return;
  528.     }
  529.     for (;; lp++) {
  530.         if (lp->name == NULL) {
  531.             fprint(2, "no such limit\n");
  532.             set(FALSE);
  533.             return;
  534.         }
  535.         if (streq(*av, lp->name))
  536.             break;
  537.     }
  538.     if (*++av == NULL)
  539.         printlimit(lp, hard);
  540.     else {
  541.         struct rlimit rlim;
  542.         long pl;
  543.         getrlimit(lp->flag, &rlim);
  544.         if ((pl = parselimit(lp, *av)) < 0) {
  545.             fprint(2, "bad limit\n");
  546.             set(FALSE);
  547.             return;
  548.         }
  549.         if (hard)
  550.             rlim.rlim_max = pl;
  551.         else
  552.             rlim.rlim_cur = pl;
  553.         if (setrlimit(lp->flag, &rlim) == -1) {
  554.             uerror("setrlimit");
  555.             set(FALSE);
  556.         } else
  557.             set(TRUE);
  558.     }
  559. }
  560. #endif
  561.